Maintaining state within anonymous functions

Share this article

I had a bit of fun with JavaScript on my personal blog over the weekend, pulling together a number of topics which I’ve covered here and in articles on SitePoint. The challenge was to provide a way of linking to any paragraph within a blog entry. The solution I came up with ended up using an unobtrusive JavaScript script, a bookmarklet and some CSS as well. You can read all of the gory details in the entry, but I’d like to dissect the bookmarklet further here.

The aim of this bookmarklet (entitled “add plink IDs”) is to find all textareas on the current page that appear to contain HTML and to then add ID attributes to any paragraph tags in the textareas that do not yet have them. For example, the following HTML:

 

<p>This is a paragraph.</p>

<p>This is another paragraph.</p>


Would become:



<p id="p-0">This is a paragraph.</p>

<p id="p-1">This is another paragraph.</p>

Here’s the full bookmarklet I used, indented for readability:

 

javascript:(function() {
  var tas = document.getElementsByTagName('textarea');
  for (var i = 0; i 

It's wrapped in an anonymous function call, a technique I described in my article on bookmarklet. It's mostly pretty straight forward, but the interesting bit is the replace call which does the actual work. Here's the code in question:



var text = ta.value.replace('<p>', function() {
  if (typeof arguments.callee.counter == 'undefined') {
    arguments.callee.counter = 0;
  }
  return '</p><p id="p-'+arguments.callee.counter++ +'">';
});

There are two tricks going on here. Firstly, JavaScript's string replace method normally takes two arguments: the string (or regular expression) to find and the string to replace it with. However, in place of a replacement string it can be provided with a function which will be executed once for every replacement made. Here I've used an anonymous function as it is more concise.

The second trick is the arguments.callee.counter bit. The 'argument' object is a built-in JavaScript object that is only available inside functions. It represents the arguments that were passed to the function, and generally behaves just like a JavaScript array. However, it also provides a 'callee' property which refers to the actual function object itself (in JavaScript, even functions are objects). This feature is only really useful in anonymous functions where there's no function name to refer to the function by.

Because JavaScript functions are objects, they can have properties. The if statement at the start of the anonymous function checks if the function's callee property has been defined yet: if it hasn't, it is initialised to 0. Then in the return statement the ++ (postincrement) operator is called on that property - it returns the property's current value but increments it so that next time it will be one higher.

The end result is that the anonymous function is called once for every paragraph tag found in the textarea, returning a paragrah tag with a new ID attribute every time. The 'counter' property is simply used to maintain state in between function calls. In fact, this could be achieved equally as well using a global variable but it's cleaner to keep the property "inside" the function which uses it to avoid unnecessary namespace pollution.

Simon WillisonSimon Willison
View Author
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week